from concurrent.futures import ThreadPoolExecutor
import uvicorn
from contextlib import asynccontextmanager
from fastapi import Body, FastAPI, File, UploadFile, HTTPException, status
from pydantic import BaseModel
import random
import threading
from lamda.client import TouchBuilder,Device
from datetime import datetime
import time
from tools.image import get_target_hole_position

class AnalysisResult(BaseModel):
    target_x_position:int

stopThread=threading.Event()
montior_thread = None
timeNow = None
ips = {
    "192.168.18.102": 1,
    "192.168.18.104": 1,
    "192.168.18.105": 1,
    "192.168.18.107": 1,
    "192.168.18.109": 1,
    "192.168.18.110": 1,
    "192.168.18.111": 1,
    "192.168.18.112": 1,
    "192.168.18.113": 1,
    "192.168.18.114": 1,
    "192.168.18.115": 1,
    "192.168.18.116": 1,
    "192.168.18.117": 1,
    "192.168.18.119": 1,
    "192.168.18.120": 1,
    "192.168.18.121": 1,
    "192.168.18.122": 1,
    "192.168.18.123": 1,
    "192.168.18.124": 1,
    "192.168.18.200":1
}

def move(d, sp):
    slideUI = d(className="android.widget.Image").get(1)
    sx = ((slideUI.get(1).info().bounds.left)+(slideUI.get(1).info().bounds.right)) // 2 
    # print("sx:",sx)
    sy = ((slideUI.get(1).info().bounds.top)+(slideUI.get(1).info().bounds.bottom)) // 2

    calibration_offset = 137  
    end_x = sp + calibration_offset
    
    # 计算需要滑动的总距离
    total_move_distance = end_x - sx

    print(f"滑块起始坐标: ({sx}, {sy})")
    print(f"识别出的缺口坐标: {sp}px")
    print(f"校准后的目标终点X坐标: {end_x}px")
    print(f"需要滑动的总距离: {total_move_distance}px")

    # 安全检查：如果计算出的距离过小或为负，说明可能出错了，避免无效操作
    if total_move_distance <= 10:
        try:
            reflash = d(text = "Refresh")
            reflash.click()
            time.sleep(3)
        except Exception as e :
            print(f"未找到刷新按钮：{e}")
            return
        # print("警告: 计算出的滑动距离过小，操作可能无效或已在目标位置。结束滑动操作。")
        # return None
    print("开始滑动")
    touch = TouchBuilder().down(x=sx, y=sy,pressure=random.randint(50,70)).wait(random.randint(150, 250))
    steps = random.randint(6, 9)
    for i in range(steps):
        progress = (i + 1) / steps
        ease_out_progress = 1 - (1 - progress) ** 3
        current_step_x = sx + total_move_distance * ease_out_progress
        random_y_offset = random.randint(-8, 8)
        wait_ms = random.randint(100, 400)
        touch.move(x=int(current_step_x), y=sy + random_y_offset,pressure=random.randint(50,70)).wait(wait_ms)
        
    # 模拟人为拖动超出距离再拖回来
    overshoot_x = end_x + random.randint(4, 10)
    touch.move(x=overshoot_x, y=sy + random.randint(-5, 5),pressure=random.randint(50,70)).wait(random.randint(700, 900))
    touch.move(x=end_x, y=sy + random.randint(-2, 2),pressure=random.randint(50,70)).wait(random.randint(500, 900))
    touch.up()
    d.perform_touch(touch.build())
    
    print("滑动操作执行完毕。")
    return True

def check_verifyCode(d):
    """
    检查验证码，并且滑动验证码
    """
    for j in range(5):
        captcha = d(resourceId="captcha-verify-image")
        
        if not captcha.exists():
            break
        print(f"开始尝试第{j+1}次")
        timeNow = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"机器：{d}出现验证码,出现时间：{timeNow}")
        d(resourceId="com.zhiliaoapp.musically:id/h3f").wait_for_exists(5*1000)
        is_gone = d(resourceId="com.zhiliaoapp.musically:id/h3f").wait_until_gone(30*1000)
        if is_gone:
            image_types = d(resourceId="captcha-verify-image").screenshot(quality=100).getvalue()
            x = get_target_hole_position(image_types)
            move(d,x)
        time.sleep(5)

def check_clock(d):
    """
    检查是否正在运行tk并亮屏
    """
    app = d.current_application()
    package_name = app.info().packageName
    if package_name != "com.zhiliaoapp.musically":
        return
    if not d.is_screen_on():
        d.wake_up()
        time.sleep(1)
        if  d.is_screen_locked():
            d().fling_from_bottom_to_top()
            print(f"设备{d}已亮屏")

def check_restart_devices(shared_ips,reboot_timestamps,lock):
    ips_to_reactivate = []
    current_time = time.time()
    with lock:
        for ip,reboot_time in reboot_timestamps.items():
            if(current_time - reboot_time > 100):
                ips_to_reactivate.append(ip)
        for ip in ips_to_reactivate:
            #print(f"{[ip]}开机完成，重新开始检测")
            shared_ips[ip] = 1
            del reboot_timestamps[ip]

def check_verifyCode_thread():
    """
    检查所有IP验证码，并处理
    """
    ips_copy = ips.copy()
    reboot_timestamps = {}
    lock = threading.Lock()

    running_tasks = set()
    #使用ThreadPooolExecutor来进行线程管理
    with ThreadPoolExecutor(max_workers=24) as executor:
        while not stopThread.is_set():
            check_restart_devices(ips_copy,reboot_timestamps,lock)
            if  reboot_timestamps.keys():
                print(f"  - 禁用设备 ({len(reboot_timestamps)}): {list(reboot_timestamps.keys())}")
            with lock:
                ips_run = [ip for ip,status in ips_copy.items()
                            if status == 1 and ip not in running_tasks
                        ]
                #timeNow = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                #print(f"{timeNow}开始一轮检测")
                for ip in ips_run:
                    running_tasks.add(ip)
                    #submit提交线程任务开始处理
                    future =  executor.submit(check_task,ip,ips_copy,reboot_timestamps,lock)
                    #设置current防止回调闭包
                    def tast_done_callback(f,current_ip = ip):
                        with lock:
                            running_tasks.remove(current_ip)
                        #print(f"{current_ip}已执行完任务，移除正在运行队列")
                    future.add_done_callback(tast_done_callback)
            stopThread.wait(5)
    print("监控线程已停止！")

def check_task(ip,ips_copy,reboot_timestamps,lock):
    """
    检查任务
    """
    if not ips_copy[ip] :
        print(f"{[ip]}已关机，跳过本轮检测")
        return
    try:
        d = Device(ip,65008)
        start_time = time.time()
        check_clock(d)
        end_time = time.time()
        duration = end_time - start_time
        #print(f"{ip}检测完成，共耗时: {duration:.2f} 秒")
        if duration > 60 :
            #d.reboot()
            print(f"{d}设备已重启")
            ips_copy[ip] = 0
            reboot_timestamps[ip] = time.time()
        stopThread.wait(1)
        check_verifyCode(d)
    except Exception as e:
        timeNow = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        print(f"[{timeNow}  {ip}]监控线程发生异常：{e}")
        with lock :
            ips_copy[ip] = 0
            reboot_timestamps[ip] = time.time() 

@asynccontextmanager
async def lifespan(app: FastAPI):
    print("FastAPI启动中")
    global montior_thread

    montior_thread = threading.Thread(target=check_verifyCode_thread)
    montior_thread.daemon= True
    montior_thread.start()
    print("监控线程已启动")

    yield
    print("结束进程")
    stopThread.set()

app = FastAPI(
    lifespan=lifespan,
    title="滑块检测"
)

@app.post(
    "/api/getSpace",
    response_model=AnalysisResult,
    tags=["Image Analysis"]
)
async def analysis_image_from_bytes(
    image_bytes:bytes = Body(...,media_type="image/png"),description="要分析的图的原始字节流。"
):
    target_x = get_target_hole_position(image_bytes)
    if target_x is None:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="在图片中未找到符合条件的目标缺口。"
        )
    return AnalysisResult(target_x_position=target_x)

if __name__ == "__main__":
    uvicorn.run(app,host="0.0.0.0",port=8000)
